package org.altervista.cp.thinjdbc.util;

import java.util.Iterator;
import java.util.LinkedList;

public abstract class Pool<T> {

	private static final LogHelper logger = new LogHelper(Pool.class);

	public abstract T newElement(Object... args);

	public Pool() {
	}

	public Pool(int initialSize, int maxSize) {
		setMaxSize(maxSize);
		setInitialSize(initialSize);
	}

	public void discard() {
		synchronized (_pool) {
			updateCount(-1);
		}
	}

	public void release(T o) {
		synchronized (_pool) {
			_pool.addLast(o);
			updateCount(-1);
			_pool.notify();
		}
	}

	public T poll(Object... args) {
		synchronized (_pool) {
			while (_elementCount == _maxSize) {
				try {
					_pool.wait();
				} catch (InterruptedException e) {
				}
			}

			T element = null;
			if (_pool.isEmpty()) {
				element = newElement(args);
			} else {
				element = _pool.removeFirst();
			}

			updateCount(1);

			return element;
		}
	}

	public void setInitialSize(int initialSize) {
		if (initialSize < 0) {
			throw new IllegalArgumentException("negative value: " + initialSize);
		}
		if (initialSize > _maxSize) {
			throw new IllegalArgumentException(
					"maxSize must be greater than initialSize");
		}
		synchronized (_pool) {
			if (initialSize > _pool.size()) {
				for (int i = _pool.size(); i < initialSize; i++) {
					_pool.add(newElement());
				}
			}
			_initialSize = initialSize;
		}
	}

	public int getInitialSize() {
		return _initialSize;
	}

	public int getMaxSize() {
		return _maxSize;
	}

	public void setMaxSize(int maxSize) {
		if (maxSize < 0) {
			throw new IllegalArgumentException("maxSize value must be > 0: "
					+ maxSize);
		}
		if (maxSize < _initialSize) {
			throw new IllegalArgumentException(
					"maxSize must be greater than initialSize");
		}

		synchronized (_pool) {
			if (maxSize < _pool.size()) {
				int i = 0;
				Iterator<T> it = _pool.iterator();
				for (; i < maxSize; i++) {
					it.next();
				}
				for (int j = i; j < _pool.size(); j++) {
					it.remove();
				}
			}
			_maxSize = maxSize;
		}
	}

	private void updateCount(int amount) {
		_elementCount += amount;
		logger.debug((amount > 0 ? "element polled, count {0}"
				: "element released, count {0}"), _elementCount);
	}

	protected LinkedList<T> _pool = new LinkedList<T>();

	private int _initialSize;
	private int _maxSize;
	private int _elementCount;

}
